home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / haeberli / mtex / crop.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  29KB  |  821 lines

  1. /*
  2.  * Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. #include <gl/gl.h>
  18. #include <math.h>
  19. #include <device.h>
  20. #include "types.h"
  21. #include "colors.h"
  22. #include "other.h"
  23.  
  24. quad_points old_crop;
  25. long prev_rb_cx,prev_rb_cy;
  26. long crop_button_indexes[4];
  27. long old_state;
  28.  
  29. Boolean crop_1_to_1;           /* Global variable! */
  30.  
  31. /* Last subscript varies most rapidly in C */
  32. /*                              Square  Rect No-rot 1:1        */
  33. /*                                  on    on    on    on       */
  34. /*                               off   off   off   off         */
  35. long crop_state_table[8][8] = { { 3, 0, 0, 2, 0, 4, 0, 7 },   /* Currently square */
  36.                 { 0, 1, 3, 0, 0, 5, 0, 8 },   /* Currently rectangle */
  37.                 { 0, 1, 0, 2, 0, 6, 0, 8 },   /* Currently quad */
  38.                 { 6, 0, 0, 5, 1, 0, 0, 7 },   /* Currently al_square */
  39.                     { 0, 4, 6, 0, 2, 0, 0, 8 },   /* Currently al_rect */
  40.                 { 0, 4, 0, 5, 3, 0, 0, 8 },   /* Currently al_quad */
  41.                 { 8, 0, 0, 8, 1, 0, 4, 0 },   /* Currently al_no_resam_square */
  42.                 { 0, 7, 6, 0, 2, 0, 5, 0 } }; /* Currently al_no_resam_rectangle */
  43.  
  44. /*                         S Re Ro 11       */
  45. long lit_table[8][4] = { { 1, 0, 0, 0 },   /* Currently square */
  46.                  { 0, 1, 0, 0 },   /* Currently rectangle */
  47.              { 0, 0, 0, 0 },   /* Currently quad */
  48.              { 1, 0, 1, 0 },   /* Currently al_square */
  49.              { 0, 1, 1, 0 },   /* Currently al_rect */
  50.              { 0, 0, 1, 0 },   /* Currently al_quad */
  51.              { 1, 0, 1, 1 },   /* Currently al_no_resam_square */
  52.              { 0, 1, 1, 1 } }; /* Currently al_no_resam_rectangle */
  53.  
  54. /*...................................................................*/
  55. /* Draw a quadrilateral as four lines, using color col...this is for rubberbanding */
  56.  
  57. void draw_qp(quad_points *qp,Colorindex col1, Colorindex col2)
  58. {
  59. point p1,p2,p3,p4,i1,i2,i3,i4;
  60. float center_x,center_y;
  61.  
  62.    color(col1);
  63.    SETPOINT(p1,(*qp)[0],(*qp)[1])
  64.    SETPOINT(p2,(*qp)[2],(*qp)[3])
  65.    SETPOINT(p3,(*qp)[4],(*qp)[5])
  66.    SETPOINT(p4,(*qp)[6],(*qp)[7])
  67.    draw_line(p1,p2);
  68.    draw_line(p2,p3);
  69.    draw_line(p3,p4);
  70.    draw_line(p4,p1);
  71.    center_x = (p1[0]+p2[0]+p3[0]+p4[0]) / 4.0;
  72.    center_y = (p1[1]+p2[1]+p3[1]+p4[1]) / 4.0;
  73.    i1[0] = p1[0] + SIGN2(p1[0],center_x);
  74.    i1[1] = p1[1] + SIGN2(p1[1],center_y);
  75.    i2[0] = p2[0] + SIGN2(p2[0],center_x);
  76.    i2[1] = p2[1] + SIGN2(p2[1],center_y);
  77.    i3[0] = p3[0] + SIGN2(p3[0],center_x);
  78.    i3[1] = p3[1] + SIGN2(p3[1],center_y);
  79.    i4[0] = p4[0] + SIGN2(p4[0],center_x);
  80.    i4[1] = p4[1] + SIGN2(p4[1],center_y);
  81.    color(col2);
  82.    draw_line(i1,i2);
  83.    draw_line(i2,i3);
  84.    draw_line(i3,i4);
  85.    draw_line(i4,i1);
  86. }
  87.  
  88. /*...................................................................*/
  89.  
  90. /* Draw a rubberband quadrilateral into the overlay planes */
  91. void draw_rb(long j)
  92. {
  93. long i;
  94. Colorindex old_color;
  95. short old_map1_red,old_map1_grn,old_map1_blu;
  96. short old_map2_red,old_map2_grn,old_map2_blu;
  97. quad_points *qp;
  98.  
  99.    drawmode(PUPDRAW);
  100.    gconfig();
  101.    old_color = getcolor();
  102.    getmcolor(1,&old_map1_red,&old_map1_grn,&old_map1_blu);
  103.    getmcolor(2,&old_map2_red,&old_map2_grn,&old_map2_blu);
  104.    mapcolor(1,255,0,255);
  105.    mapcolor(2,0,0,0);
  106.    draw_qp(&old_crop,0,0);
  107.    qp = (quad_points *)(ipt[j].value);
  108.    draw_qp(qp,1,2);
  109.    color(old_color);
  110.    mapcolor(1,old_map1_red,old_map1_grn,old_map1_blu);
  111.    mapcolor(2,old_map2_red,old_map2_grn,old_map2_blu);
  112.    drawmode(NORMALDRAW);
  113.    gconfig();
  114.    for (i=0; i<8; i++) old_crop[i] = (*qp)[i];
  115. }
  116.  
  117. /*...................................................................*/
  118.  
  119. void take_down_rb()
  120. {
  121. Colorindex old_color;
  122.  
  123.    drawmode(OVERDRAW);
  124.    gconfig();
  125.    old_color = getcolor();
  126.    draw_qp(&old_crop,0,0);
  127.    color(old_color);
  128.    drawmode(NORMALDRAW);
  129.    gconfig();
  130. }
  131.  
  132. /*...................................................................*/
  133. /* Return +1 or -1 if p1-->tp is clockwise or counter-clockwise from p1-->p2 */
  134.  
  135. long in_out(point p1, point p2, point tp)
  136. {
  137. point v12,v1p;
  138.  
  139.    SUB(p1,p2,v12);
  140.    SUB(p1,tp,v1p);
  141.    return(SIGN(CROSS(v12,v1p)));
  142. }
  143.    
  144. /*...................................................................*/
  145. /* Return TRUE or FALSE if cx,cy is inside or outside of quadrilateral *qp */
  146.  
  147. Boolean outside_qp(quad_points *qp, long cx, long cy)
  148. {
  149. point p1,p2,p3,p4,tp;
  150. long s12,s23,s34,s41,sum;
  151.  
  152.    SETPOINT(p1,(*qp)[0],(*qp)[1])
  153.    SETPOINT(p2,(*qp)[2],(*qp)[3])
  154.    SETPOINT(p3,(*qp)[4],(*qp)[5])
  155.    SETPOINT(p4,(*qp)[6],(*qp)[7])
  156.    SETPOINT(tp,(float)cx,(float)cy)
  157.    s12 = in_out(p1,p2,tp);
  158.    s23 = in_out(p2,p3,tp);
  159.    s34 = in_out(p3,p4,tp);
  160.    s41 = in_out(p4,p1,tp);
  161.    sum = s12+s23+s34+s41;
  162.    return ((sum != -4) && (sum != 4));
  163. }
  164. /*...................................................................*/
  165. /* Extend atan2f to return OK even if x and or y are zero */
  166.  
  167. float arctan(float x, float y)
  168. {
  169.  
  170.    if ((x != 0.0) && (y != 0.0)) return(atan2f(y,x));
  171.    if ((x == 0.0) && (y == 0.0)) return(0.0);
  172.    if ((x > 0.0) && (y == 0.0)) return(0.0);
  173.    if ((x < 0.0) && (y == 0.0)) return(3.1415926535897932384626433);
  174.    if ((x == 0.0) && (y > 0.0)) return(3.1415926535897932384626433/2.0);
  175.    return(-3.1415926535897932384626433/2.0);
  176. }
  177.  
  178. /*...................................................................*/
  179. /* Normalize a vector...do nothing if zero length */
  180.  
  181. void normalize(point *v)
  182. {
  183. float len;
  184.  
  185.    len = LENGTH(*v);
  186.    if (len == 0) return;
  187.    (*v)[0] /= len;
  188.    (*v)[1] /= len;
  189. }
  190.  
  191. /*...................................................................*/
  192. /* Return TRUE or FALSE if x,y is outside 512x512 image on-screen */
  193. /* Also return TRUE if any side is too short. */
  194. /* If TRUE, reset qp to original value to prevent rubberband going outside */
  195.  
  196. Boolean not_in_image(quad_points *qp, float x, float y)
  197. {
  198. long i,k,max_screen_x,max_screen_y;
  199. Boolean reject;
  200. float dx,dy,manhattan_dist,min_manhattan_dist,zoom;
  201.  
  202.    if (improved_image->interior_xsize > improved_image->interior_ysize)
  203.       {
  204.       zoom = 512.0 / (float)improved_image->interior_xsize;
  205.       max_screen_x = 512 + IMAGE_BORDER;
  206.       max_screen_y = (long)(zoom * improved_image->interior_ysize + IMAGE_BORDER);
  207.       }
  208.    else
  209.       {
  210.       zoom = 512.0 / (float)improved_image->interior_ysize;
  211.       max_screen_x = (long)(zoom * improved_image->interior_xsize + IMAGE_BORDER);
  212.       max_screen_y = 512 + IMAGE_BORDER;
  213.       }
  214.    reject = (x < IMAGE_BORDER) || (x > 512+IMAGE_BORDER) ||
  215.             (y < IMAGE_BORDER) || (y > 512+IMAGE_BORDER);
  216.    min_manhattan_dist = 999999999.;
  217.    for (i=0; i<8; i+=2)
  218.       {
  219.       if ((i+2) < 8) k = i; else k = i-8;
  220.       dx = (*qp)[i+0] - (*qp)[k+2];
  221.       dy = (*qp)[i+1] - (*qp)[k+3];
  222.       manhattan_dist = ABS(dx) + ABS(dy);
  223.       min_manhattan_dist = MIN2(manhattan_dist,min_manhattan_dist);
  224.       }
  225.    reject |= (min_manhattan_dist < 10.);
  226.    if (reject)
  227.          {
  228.          for (k=0; k<8; k++) (*qp)[k] = old_crop[k];
  229.      return(TRUE);
  230.          }
  231.    else return(FALSE);
  232. }
  233. /*...................................................................*/
  234. /*...useful someday?....
  235.    len_side1 =  sqrtf(SQR((*qp)[0] - (*qp)[2]) + SQR((*qp)[1] - (*qp)[3]));
  236.    len_side2 =  sqrtf(SQR((*qp)[2] - (*qp)[4]) + SQR((*qp)[3] - (*qp)[5]));
  237.    len_side3 =  sqrtf(SQR((*qp)[4] - (*qp)[6]) + SQR((*qp)[5] - (*qp)[7]));
  238.    len_side4 =  sqrtf(SQR((*qp)[6] - (*qp)[0]) + SQR((*qp)[7] - (*qp)[1]));
  239.    avg_side = (len_side1 + len_side2 + len_side3 + len_side4) / 4.0; 
  240.    diag13 =  sqrtf(SQR((*qp)[0] - (*qp)[4]) + SQR((*qp)[1] - (*qp)[5]));
  241.    diag24 =  sqrtf(SQR((*qp)[2] - (*qp)[6]) + SQR((*qp)[3] - (*qp)[7]));
  242.    avg_diag = (diag13 + diag24) / 2.0;
  243. */
  244. /*...................................................................*/
  245.  
  246. Boolean force_onscreen(quad_points *qp)
  247. {
  248. float minx,miny,maxx,maxy,movex1,movey1,movex2,movey2,zoom;
  249. long i,max_screen_x,max_screen_y;
  250.  
  251.    minx = MIN4((*qp)[0],(*qp)[2],(*qp)[4],(*qp)[6]);
  252.    miny = MIN4((*qp)[1],(*qp)[3],(*qp)[5],(*qp)[7]);
  253.    maxx = MAX4((*qp)[0],(*qp)[2],(*qp)[4],(*qp)[6]);
  254.    maxy = MAX4((*qp)[1],(*qp)[3],(*qp)[5],(*qp)[7]);
  255.    if (improved_image->interior_xsize > improved_image->interior_ysize)
  256.       {
  257.       zoom = 512.0 / (float)improved_image->interior_xsize;
  258.       max_screen_x = 512 + IMAGE_BORDER;
  259.       max_screen_y = (long)(zoom * improved_image->interior_ysize + IMAGE_BORDER);
  260.       }
  261.    else
  262.       {
  263.       zoom = 512.0 / (float)improved_image->interior_ysize;
  264.       max_screen_x = (long)(zoom * improved_image->interior_xsize + IMAGE_BORDER);
  265.       max_screen_y = 512 + IMAGE_BORDER;
  266.       }
  267.    movex1 = movex2 = 0.0;
  268.    movey1 = movey2 = 0.0;
  269.    if (minx < IMAGE_BORDER) movex1 = minx - IMAGE_BORDER;
  270.    if (miny < IMAGE_BORDER) movey1 = miny - IMAGE_BORDER;
  271.    if (maxx > max_screen_x) movex2 = maxx - 512+IMAGE_BORDER;
  272.    if (maxy > max_screen_x) movey2 = maxy - 512+IMAGE_BORDER;
  273.    if (movex1 != 0.0 && movex2 != 0.0) return(FALSE);
  274.    if (movey1 != 0.0 && movey2 != 0.0) return(FALSE);
  275.    for (i=0; i<4; i++)
  276.       {
  277.       (*qp)[2*i+0] -= (movex1 + movex2);
  278.       (*qp)[2*i+1] -= (movey1 + movex2);
  279.       }
  280.    return(TRUE);
  281. }
  282.  
  283. /*...................................................................*/
  284.  
  285. void force_square(quad_points *qp)
  286. {
  287. point center,vect_v1,vect_v2,vect_v3,vect_v4,vect_typ_v;
  288.  
  289.    center[0] = ((*qp)[0] + (*qp)[2] + (*qp)[4] + (*qp)[6]) / 4.0;  /* Center = average of vertexes */
  290.    center[1] = ((*qp)[1] + (*qp)[3] + (*qp)[5] + (*qp)[7]) / 4.0;
  291.    vect_v1[0] =  (*qp)[0] - center[0];
  292.    vect_v1[1] =  (*qp)[1] - center[1];
  293.    vect_v2[0] =  (*qp)[3] - center[1];      /* Reflect X <-->  Y  */
  294.    vect_v2[1] = -(*qp)[2] + center[0];      /* Reflect X <-->  Y  */
  295.    vect_v3[0] = -(*qp)[4] + center[0];      /* Reflect X <--> -X */
  296.    vect_v3[1] = -(*qp)[5] + center[1];      /* Reflect Y <--> -Y */
  297.    vect_v4[0] = -(*qp)[7] + center[1];      /* Reflect X <--> -Y */
  298.    vect_v4[1] =  (*qp)[6] - center[0];      /* Reflect Y <--> -X */
  299.    vect_typ_v[0] = AVG4(vect_v1[0],vect_v2[0],vect_v3[0],vect_v4[0]);
  300.    vect_typ_v[1] = AVG4(vect_v1[1],vect_v2[1],vect_v3[1],vect_v4[1]);
  301.    (*qp)[0] =  vect_typ_v[0] + center[0];
  302.    (*qp)[1] =  vect_typ_v[1] + center[1];
  303.    (*qp)[2] = -vect_typ_v[1] + center[0];
  304.    (*qp)[3] =  vect_typ_v[0] + center[1];
  305.    (*qp)[4] = -vect_typ_v[0] + center[0];
  306.    (*qp)[5] = -vect_typ_v[1] + center[1];
  307.    (*qp)[6] =  vect_typ_v[1] + center[0];
  308.    (*qp)[7] = -vect_typ_v[0] + center[1];
  309.    force_onscreen(qp);
  310.    draw_rb(rb_ipt_index);
  311. }
  312.  
  313. /*...................................................................*/
  314.  
  315. void rotate_by_angle(quad_points *qp, float rotation_angle)
  316. {
  317. point center,center_vertex;
  318. long i;
  319. float radius,prev_angle,new_angle,x,y;
  320.  
  321.    center[0] = ((*qp)[0] + (*qp)[2] + (*qp)[4] + (*qp)[6]) / 4.0;  /* Center = average of vertexes */
  322.    center[1] = ((*qp)[1] + (*qp)[3] + (*qp)[5] + (*qp)[7]) / 4.0;
  323.    for (i=0; i<4; i++)
  324.       {
  325.       center_vertex[0] = (*qp)[2*i+0] - center[0];       /* Vector to vertex from center */
  326.       center_vertex[1] = (*qp)[2*i+1] - center[1];
  327.       radius = LENGTH(center_vertex);                 /* Convert vertex to polar coord. */
  328.       prev_angle = arctan(center_vertex[0],center_vertex[1]);
  329.       new_angle = prev_angle + rotation_angle;               /* Add rotation angle */
  330.       x = (*qp)[2*i+0] = center[0] + radius * cosf(new_angle);  /* Convert to cartesian */
  331.       y = (*qp)[2*i+1] = center[1] + radius * sinf(new_angle);
  332.       if (not_in_image(qp,x,y)) return;    /* Reset & abort if any vertex outside image */
  333.       }
  334. }
  335.  
  336. /*...................................................................*/
  337. /* Rotate quadrilateral around its center to track angular movement of cursor */
  338.  
  339. void rotate_quad(quad_points *qp, long cx, long cy)
  340. {
  341. float theta_center_cursor,theta_center_prevcursor,rotation_angle;
  342. point center,center_cursor,center_prevcursor;
  343.  
  344.    center[0] = ((*qp)[0] + (*qp)[2] + (*qp)[4] + (*qp)[6]) / 4.0;               /* Center = average of vertexes */
  345.    center[1] = ((*qp)[1] + (*qp)[3] + (*qp)[5] + (*qp)[7]) / 4.0;
  346.    center_cursor[0] = center[0] - (float)cx;                                    /* center_cursor = vector: center-->cursor */
  347.    center_cursor[1] = center[1] - (float)cy;
  348.    center_prevcursor[0] = center[0] - (float)prev_rb_cx;                        /* center_prevcursor = vector: center-->previous cursor */
  349.    center_prevcursor[1] = center[1] - (float)prev_rb_cy;
  350.    theta_center_cursor = arctan(center_cursor[0],center_cursor[1]);             /* Angle to cursor */
  351.    theta_center_prevcursor = arctan(center_prevcursor[0],center_prevcursor[1]); /* Angle to previous cursor */
  352.    rotation_angle = theta_center_cursor - theta_center_prevcursor;
  353.    rotate_by_angle(qp,rotation_angle);
  354. }
  355.  
  356. /*...................................................................*/
  357. /* Move "nearest" vertex to be at cursor, but reset if x,y outside image */
  358.  
  359. void move_vertex_quad(quad_points *qp, long cx,long cy, long which_vert)
  360. {
  361. float x,y;
  362. long i,k;
  363.  
  364.    k = 2 * (which_vert-1);        /* 0,2,4,6 for vertex 1,2,3,4 */
  365.    x = (*qp)[k] += (float)(cx - prev_rb_cx);
  366.    y = (*qp)[k+1] += (float)(cy - prev_rb_cy);
  367.    if (not_in_image(qp,x,y)) return;
  368. }
  369.  
  370. /*...................................................................*/
  371. /* Move all four qp vertexes by motion of cursor, abort & reset if any go too far */
  372.  
  373. void move_whole_quad(quad_points *qp, long cx, long cy)
  374. {
  375. float diff_x,diff_y,x,y;
  376. long i;
  377.    
  378.    diff_x = (float)(cx - prev_rb_cx);
  379.    diff_y = (float)(cy - prev_rb_cy);
  380.    for (i=0; i<8; i+=2)
  381.       {
  382.       x = (*qp)[i] += diff_x;
  383.       y = (*qp)[i+1] += diff_y;
  384.       if (not_in_image(qp,x,y)) return;
  385.       }
  386. }
  387. /*...................................................................*/
  388. /* Distance squared from point to line.   See Graphic Gems II, Page 10. */
  389. /* pa and pb are points on line.  Distance is to line, not line segment */
  390. /* This works because crossproduct = twice area of abc triangle.        */
  391.  
  392. float dist_sq_to_line(point pc, point pa, point pb)
  393. {
  394. point pcr,pbr;
  395. float area2,ds;
  396.  
  397.    SUB(pc,pa,pcr)
  398.    SUB(pb,pa,pbr)
  399.    area2 = CROSS(pcr,pbr);
  400.    ds = SQR(area2) / LENGTH_SQ(pbr);
  401.    return(ds);
  402.    }
  403.    
  404. /*...................................................................*/
  405. /* Move one side of quadrilateral by motion of cursor along a vector which */
  406. /* is more or less perpendicular to that side.  Actual vector is average   */
  407. /* of the two adjacent sides.  Reset if side moves outside image.          */
  408.  
  409. void move_side_quad(quad_points *qp, long cx, long cy, long side)
  410. {
  411. float diff_x,diff_y,cursor_movement_length,cosine,distance_to_move;
  412. point cursor_movement,p1,p2,p3,p4,movement_vect;
  413. point adjacent_side1_vect,adjacent_side2_vect,avg_adjacent_side_vect;
  414. long k1,k2,k3,k4;
  415.    diff_x = (float)(cx - prev_rb_cx);
  416.    diff_y = (float)(cy - prev_rb_cy);
  417.    SETPOINT(cursor_movement,diff_x,diff_y)        /* Cursor movement vector */
  418.    cursor_movement_length = LENGTH(cursor_movement);
  419.    
  420. /* Which side is cx,cy nearest to? */
  421.  
  422.    
  423. /* Side  k1  k2  k3  k4   Previous Side       Moving Side         Next Side       */
  424. /*   1   3   0   1   2  qp[6,7]-->qp[0,1]  qp[0,1]-->qp[2,3]  qp[2,3]-->qp[4,5]   */
  425. /*   2   0   1   2   3  qp[0,1]-->qp[2,3]  qp[2,3]-->qp[4,5]  qp[4,5]-->qp[6,7]   */
  426. /*   3   1   2   3   0  qp[2,3]-->qp[4,5]  qp[4,5]-->qp[6,7]  qp[6,7]-->qp[0,1]   */
  427. /*   4   2   3   0   1  qp[4,5]-->qp[6,7]  qp[6,7]-->qp[0,1]  qp[0,1]-->qp[2,3]   */
  428. /* In general:            p1  -->  p2        p2  -->   p3       p3   -->  p4      */
  429.  
  430.    k1 = (side+2) % 4;                   /* Copy chosen qp points into p1,p2,p3,p4 */
  431.    k2 = (side+3) % 4;
  432.    k3 = (side+0) % 4;
  433.    k4 = (side+1) % 4;
  434.    SETPOINT(p1, (*qp)[2*k1], (*qp)[2*k1+1])
  435.    SETPOINT(p2, (*qp)[2*k2], (*qp)[2*k2+1])
  436.    SETPOINT(p3, (*qp)[2*k3], (*qp)[2*k3+1])
  437.    SETPOINT(p4, (*qp)[2*k4], (*qp)[2*k4+1])
  438.    
  439.    SUB(p1,p2,adjacent_side1_vect)       /* We will move in average of adjacent sides */
  440.    SUB(p4,p3,adjacent_side2_vect)
  441.    AVG(adjacent_side1_vect,adjacent_side2_vect,avg_adjacent_side_vect)
  442.    
  443.    normalize(&cursor_movement);
  444.    normalize(&avg_adjacent_side_vect);
  445.    cosine = DOT(cursor_movement,avg_adjacent_side_vect);        /* Projection */
  446.    distance_to_move = cosine * cursor_movement_length;          /* Movement vector length */
  447.    MULT(avg_adjacent_side_vect,distance_to_move,movement_vect)  /* Movement vector itself */
  448.    (*qp)[2*k2] =   p2[0] + movement_vect[0];                    /* Add movement */   
  449.    (*qp)[2*k2+1] = p2[1] + movement_vect[1];
  450.    if (not_in_image(qp,(*qp)[2*k2],(*qp)[2*k2+1])) return;       /* Abort & Reset if outside */
  451.    (*qp)[2*k3] =   p3[0] + movement_vect[0];                    /* Add movement */   
  452.    (*qp)[2*k3+1] = p3[1] + movement_vect[1];
  453.    not_in_image(qp,(*qp)[2*k3],(*qp)[2*k3+1]);                  /* Reset if outside */
  454. }
  455.  
  456. /*...................................................................*/
  457. /* Move vertex to resize rectangle...keep adjacent sides parallel to others */
  458.  
  459. void resize_rect(quad_points *qp, long cx, long cy, long which_vert)
  460. {
  461. long k1,k2,k3,k4;
  462. float len_new_diagonal,cosine,length_41,length_43,x,y,diff_x,diff_y,length_cm;
  463. point p1,p2,p3,p4,new_p1,new_p2,new_p3,new_diagonal,p4_p1_vect,p4_p3_vect;
  464. point cursor_move,old_diagonal;
  465.    
  466. /* Move "which_vert" vertex to cx,cy */
  467. /* Determine adjacent vertexes */
  468. /* They must be where cx,cy project on to original opposite sides */
  469.  
  470.    k1 = (which_vert+2) % 4;                    /* Copy chosen qp points into p1,p2,p3,p4 */
  471.    k2 = (which_vert+3) % 4;                    /* "which_vert = p2 */
  472.    k3 = (which_vert+0) % 4;
  473.    k4 = (which_vert+1) % 4;                    /* ..p1 ... p4   */
  474.    SETPOINT(p1, (*qp)[2*k1], (*qp)[2*k1+1])    /*    .     /.   */
  475.    SETPOINT(p2, (*qp)[2*k2], (*qp)[2*k2+1])    /*    .    / .   */
  476.    SETPOINT(p3, (*qp)[2*k3], (*qp)[2*k3+1])    /*    .   /  .   */
  477.    SETPOINT(p4, (*qp)[2*k4], (*qp)[2*k4+1])    /*   p2 ./. p3   */
  478.                                                /*      /    .   */
  479.    diff_x = (float)(cx - prev_rb_cx);          /*     *     .   */  
  480.    diff_y = (float)(cy - prev_rb_cy);
  481.    if ((crop_constraints == SQUARE) ||
  482.        (crop_constraints == AL_SQUARE) ||
  483.        (crop_constraints == AL_NO_RESAM_SQUARE))
  484.       {
  485.       SETPOINT(cursor_move,diff_x,diff_y)
  486.       SUB(p2,p4,old_diagonal)
  487.       length_cm = LENGTH(cursor_move);
  488.       normalize(&cursor_move);
  489.       normalize(&old_diagonal);
  490.       cosine = DOT(cursor_move,old_diagonal);
  491.       diff_x = old_diagonal[0] * length_cm * cosine;
  492.       diff_y = old_diagonal[1] * length_cm * cosine;
  493.       }
  494.    x = (*qp)[2*k2] +=   diff_x;        
  495.    y = (*qp)[2*k2+1] += diff_y;
  496.    if (not_in_image(qp,x,y)) return;         /* Abort if outside */
  497.    SETPOINT(new_p2,x,y)
  498.    SUB(new_p2,p4,new_diagonal)
  499.    len_new_diagonal = LENGTH(new_diagonal);
  500.    SUB(p1,p4,p4_p1_vect)
  501.    SUB(p3,p4,p4_p3_vect)
  502.    normalize(&new_diagonal);
  503.    normalize(&p4_p1_vect);
  504.    normalize(&p4_p3_vect);
  505.    cosine = DOT(p4_p1_vect,new_diagonal);
  506.    length_41 = cosine * len_new_diagonal;
  507.    MULT(p4_p1_vect,length_41,new_p1)
  508.    x = (*qp)[2*k1] =   new_p1[0] + p4[0];
  509.    y = (*qp)[2*k1+1] = new_p1[1] + p4[1];
  510.    if (not_in_image(qp,x,y)) return;
  511.    cosine = DOT(p4_p3_vect,new_diagonal);
  512.    length_43 = cosine * len_new_diagonal;
  513.    MULT(p4_p3_vect,length_43,new_p3)
  514.    x = (*qp)[2*k3] =   new_p3[0] + p4[0];
  515.    y = (*qp)[2*k3+1] = new_p3[1] + p4[1];
  516.    not_in_image(qp,x,y);
  517. }
  518.  
  519. /*...................................................................*/
  520.  
  521.  
  522. /*...................................................................*/
  523. /* Return 1,2,3,4 if corresponding side is close to cx,cy      */
  524. /* Return 0 if not.  "Close" = 1/4 of larger diagonal distance */
  525.  
  526. long near_side(quad_points *qp, long cx, long cy)
  527. {
  528. float d12,d23,d34,d41,limit,min_d,shorter_across;
  529. point cursor,p1,p2,p3,p4;
  530.  
  531.    SETPOINT(cursor,(float)cx,(float)cy)
  532.    SETPOINT(p1,(*qp)[0],(*qp)[1])
  533.    SETPOINT(p2,(*qp)[2],(*qp)[3])
  534.    SETPOINT(p3,(*qp)[4],(*qp)[5])
  535.    SETPOINT(p4,(*qp)[6],(*qp)[7])
  536.    d12 = dist_sq_to_line(cursor,p1,p2);
  537.    d23 = dist_sq_to_line(cursor,p2,p3);
  538.    d34 = dist_sq_to_line(cursor,p3,p4);
  539.    d41 = dist_sq_to_line(cursor,p4,p1);
  540.    min_d = MIN4(d12,d23,d34,d41);
  541.    shorter_across = MIN2(d12+d34,d23+d41);
  542.    limit = shorter_across / 10.;
  543.    if (min_d > limit) return(0);
  544.    if (min_d == d12) return(1);
  545.    if (min_d == d23) return(2);
  546.    if (min_d == d34) return(3);
  547.    return(4);
  548. }
  549.  
  550. /*...................................................................*/
  551. /* Return 1,2,3,4 if corresponding qp vertex is close to cx,cy */
  552. /* Return 0 if not.  "Close" = 1/4 of larger diagonal distance */
  553.  
  554. long near_vertex(quad_points *qp, long cx, long cy)
  555. {
  556. float d1,d2,d3,d4,d12,d23,d34,d41,shortest_side,radius,min_dist_to_vertex;
  557. point cursor,p1,p2,p3,p4;
  558.  
  559.    SETPOINT(cursor,(float)cx,(float)cy)
  560.    SETPOINT(p1,(*qp)[0],(*qp)[1])
  561.    SETPOINT(p2,(*qp)[2],(*qp)[3])
  562.    SETPOINT(p3,(*qp)[4],(*qp)[5])
  563.    SETPOINT(p4,(*qp)[6],(*qp)[7])
  564.    d1 = DISTSQ(p1,cursor);
  565.    d2 = DISTSQ(p2,cursor);
  566.    d3 = DISTSQ(p3,cursor);
  567.    d4 = DISTSQ(p4,cursor);
  568.    d12 = MAN_DIST(p1,p2);
  569.    d23 = MAN_DIST(p2,p3);
  570.    d34 = MAN_DIST(p3,p4);
  571.    d41 = MAN_DIST(p4,p1);
  572.    shortest_side = MIN4(d12,d23,d34,d41);
  573.    radius = SQR(shortest_side) / 40.;
  574.    min_dist_to_vertex = MIN4(d1,d2,d3,d4);
  575.    if (min_dist_to_vertex > radius) return(0);
  576.    if (min_dist_to_vertex == d1) return(1);
  577.    if (min_dist_to_vertex == d2) return(2);
  578.    if (min_dist_to_vertex == d3) return(3);
  579.    return(4);
  580. }
  581.  
  582. /*...................................................................*/
  583.  
  584. void rb_action(Boolean resize, Boolean side_move, Boolean rotate, Boolean vert_move,
  585.                quad_points *qp, long cx, long cy)
  586. {
  587. long nearest_v,nearest_s;
  588.  
  589.    if (rotate)
  590.       {
  591.       if (outside_qp(qp,cx,cy))
  592.          {
  593.      rotate_quad(qp,cx,cy);
  594.      crop_1_to_1 = FALSE;
  595.      return;
  596.      }
  597.       }
  598.    nearest_v = near_vertex(qp,prev_rb_cx,prev_rb_cy);
  599.    if (vert_move && (nearest_v > 0))
  600.       {
  601.       move_vertex_quad(qp,cx,cy,nearest_v);
  602.       crop_1_to_1 = FALSE;
  603.       return;
  604.       }
  605.    nearest_s = near_side(qp,prev_rb_cx,prev_rb_cy);
  606.    if (side_move && (nearest_v == 0) && (nearest_s > 0))
  607.       {
  608.       move_side_quad(qp,cx,cy,nearest_s);
  609.       crop_1_to_1 = FALSE;
  610.       return;
  611.       }
  612.    if (resize && (nearest_v > 0))
  613.       {
  614.       resize_rect(qp,cx,cy,nearest_v);
  615.       crop_1_to_1 = FALSE;
  616.       return;
  617.       }
  618.    move_whole_quad(qp,cx,cy);
  619. }
  620.  
  621. /*...................................................................*/
  622. /* Update procedure for rubberband widget. */
  623.  
  624. void move_rb(long j, long dev_mask, long cx, long cy)
  625. {
  626. Boolean near,far,down,up,d;
  627. quad_points *qp;
  628. long nearest;
  629.  
  630.    near = (dev_mask & NEAR) != 0;
  631.    far = (dev_mask & FAR) != 0;
  632.    down = (dev_mask & LEFTMOUSE_DOWN) != 0;
  633.    up = (dev_mask & LEFTMOUSE_UP) != 0;
  634.    if (near && !down && !up) up = !(down = getbutton(LEFTMOUSE));
  635.       
  636.  
  637.    if ((ipt[j].state == IDLE_UP) && near)         /* Enter image area...button up */
  638.       {
  639.       if (!down && !up) down = getbutton(LEFTMOUSE);
  640.       if (down) 
  641.          ipt[j].state = HL_DOWN;
  642.       else 
  643.          ipt[j].state = HL_UP;
  644.       ipt[j].input_mask1 = LEFTMOUSE_DOWN | FAR;
  645.       }
  646.    else if ((ipt[j].state == HL_UP) && far)       /* Leave image area...button up */
  647.       {
  648.       ipt[j].state = IDLE_UP;
  649.       ipt[j].input_mask1 = LEFTMOUSE_DOWN | NEAR;
  650.       }
  651.    else if ((ipt[j].state == HL_UP) && down)      /* Button goes down in image */
  652.       {
  653.       ipt[j].state = HL_DOWN;
  654.       ipt[j].input_mask1 = LEFTMOUSE_UP | NEAR | FAR;
  655.       prev_rb_cx = cx;
  656.       prev_rb_cy = cy;
  657.       }
  658.    else if ((ipt[j].state == HL_DOWN) && up)      /* Button goes up in image */
  659.       {
  660.       ipt[j].state = HL_UP;
  661.       ipt[j].input_mask1 = LEFTMOUSE_DOWN | FAR;
  662.       }
  663.    else if ((ipt[j].state == HL_DOWN) && far)     /* Leave image area...button down */
  664.       {
  665.       ipt[j].state = IDLE_UP;
  666.       ipt[j].input_mask1 = NEAR;
  667.       }
  668.    if (ipt[j].state == HL_DOWN)                   /* Take Action!  Button down while in image */
  669.       {
  670.       qp = (quad_points *)(ipt[j].value);
  671.       
  672. /* Actions = resize_rectangle, move_side, rotate, move_vertex */
  673.  
  674.       switch(ipt[j].data)                         /* Constraints determine action */
  675.          {
  676.      case(0):                     printf("Rubberband w/constraints = 0\n");
  677.      case(SQUARE):                rb_action(1,0,1,0,qp,cx,cy); break;
  678.      case(RECTANGLE):             rb_action(1,1,1,0,qp,cx,cy); break;
  679.      case(QUAD):                  rb_action(0,1,1,1,qp,cx,cy); break;
  680.      case(AL_SQUARE):             rb_action(1,0,0,0,qp,cx,cy); break;
  681.      case(AL_RECTANGLE):          rb_action(1,1,0,0,qp,cx,cy); break;
  682.      case(AL_QUAD):               rb_action(0,1,0,1,qp,cx,cy); break;
  683.      case(AL_NO_RESAM_SQUARE):    rb_action(0,0,0,0,qp,cx,cy); break;
  684.      case(AL_NO_RESAM_RECTANGLE): rb_action(0,0,0,0,qp,cx,cy); break;
  685.      default:                     printf("Unknown constraints: %d\n",ipt[j].data);
  686.          }
  687.       draw_rb(j);
  688.       prev_rb_cx = cx;
  689.       prev_rb_cy = cy;
  690.       }
  691. }
  692.  
  693. /*...................................................................*/
  694.  
  695. void fix_indicators()
  696. {
  697. long i,k;
  698.    for (i=0; i<4; i++)
  699.       {
  700.       k = crop_button_indexes[i];
  701.       ipt[k].data = lit_table[crop_constraints-1][i];
  702.       ipt[k].redisplay_ui_proc(k);
  703.       }
  704. }
  705.  
  706. /*...................................................................*/
  707.  
  708. Boolean fix_shape()
  709. {
  710. quad_points *qp;
  711. float zoom,xsize,ysize;
  712. long largest_dim;
  713.  
  714. /* Zoom is ratio of qp coordinate scale (512 wide) to texmap scale (interior_xsize wide) */
  715.  
  716.    largest_dim = MAX2(improved_image->interior_xsize,improved_image->interior_ysize);
  717.    zoom = 512.0/(float)largest_dim;
  718.    qp = (quad_points *)(ipt[rb_ipt_index].value);
  719.    xsize = (float)crop_interior_x * zoom;
  720.    ysize = (float)crop_interior_y * zoom;
  721.    (*qp)[0] = (*qp)[6] = IMAGE_BORDER + 256. - xsize / 2.0;
  722.    (*qp)[1] = (*qp)[3] = IMAGE_BORDER + 256. - ysize / 2.0;;
  723.    (*qp)[2] = (*qp)[4] = IMAGE_BORDER + 256. + xsize / 2.0;;
  724.    (*qp)[5] = (*qp)[7] = IMAGE_BORDER + 256. + ysize / 2.0;;
  725.    if (xsize > 512. || ysize > 512.)
  726.       {
  727.       crop_1_to_1 = FALSE;
  728.       (*qp)[0] = (*qp)[1] = (*qp)[3] = (*qp)[6] = IMAGE_BORDER+50.;
  729.       (*qp)[2] = (*qp)[4] = (*qp)[5] = (*qp)[7] = IMAGE_BORDER+512.-50.;
  730.       draw_rb(rb_ipt_index);
  731.       return(FALSE);
  732.       }
  733.    draw_rb(rb_ipt_index);
  734.    return(TRUE);
  735. }
  736.  
  737. /*...................................................................*/
  738.  
  739. void crop_sq(long j)
  740. {
  741.    old_state = crop_constraints;
  742.    ipt[j].data ^= 1;                                 /* Toggle button state */
  743.    crop_constraints = crop_state_table[crop_constraints-1][ipt[j].data + 0];
  744.    ipt[rb_ipt_index].data = crop_constraints;
  745.    if (crop_constraints == 0)
  746.       {
  747.       printf("Crop_sq state machine failure, old_state = %d, data = %d\n",old_state,ipt[j].data);
  748.       exit(2);
  749.       }
  750.    fix_indicators();
  751.    if (ipt[j].data == 1) force_square((quad_points *)(ipt[rb_ipt_index].value));
  752. }
  753.  
  754. /*...................................................................*/
  755.  
  756. void crop_re(long j)
  757. {
  758.    old_state = crop_constraints;
  759.    ipt[j].data ^= 1;                                 /* Toggle button state */
  760.    crop_constraints = crop_state_table[crop_constraints-1][ipt[j].data + 2];
  761.    ipt[rb_ipt_index].data = crop_constraints;
  762.    if (crop_constraints == 0)
  763.       {
  764.       printf("Crop_re state machine failure, old_state = %d, data = %d\n",old_state,ipt[j].data);
  765.       exit(2);
  766.       }
  767.    fix_indicators();
  768.    if (ipt[j].data == 1) fix_shape();
  769. }
  770.  
  771. /*...................................................................*/
  772.  
  773. void crop_nr(long j)
  774. {
  775.    old_state = crop_constraints;
  776.    ipt[j].data ^= 1;                                 /* Toggle button state */
  777.    crop_constraints = crop_state_table[crop_constraints-1][ipt[j].data + 4];
  778.    ipt[rb_ipt_index].data = crop_constraints;
  779.    if (crop_constraints == 0)
  780.       {
  781.       printf("Crop_nr state machine failure, old_state = %d, data = %d\n",old_state,ipt[j].data);
  782.       exit(2);
  783.       }
  784.    fix_indicators();
  785.    if (ipt[j].data == 1) fix_shape();
  786. }
  787.  
  788. /*...................................................................*/
  789.  
  790. void crop_11(long j)
  791. {
  792.    ipt[j].data ^= 1;                                 /* Toggle button state */
  793.    if (ipt[j].data == 1) 
  794.       if (!fix_shape())
  795.          {
  796.      ipt[j].data = 0;
  797.      return;
  798.      }
  799.    old_state = crop_constraints;
  800.    crop_1_to_1 = ipt[j].data;
  801.    crop_constraints = crop_state_table[crop_constraints-1][ipt[j].data + 6];
  802.    ipt[rb_ipt_index].data = crop_constraints;
  803.    fix_indicators();
  804.    if (crop_constraints == 0)
  805.       {
  806.       printf("Crop_11 state machine failure, old_state = %d, data = %d\n",old_state,ipt[j].data);
  807.       exit(2);
  808.       }
  809. }
  810.  
  811. /*...................................................................*/
  812.  
  813. void cleanup_crop()
  814. {
  815.    take_down_rb();
  816.    turn_off_all_widgets();
  817. }
  818.  
  819.  
  820. /*...................................................................*/
  821.